package com.linkoog.devtools.intellij.ui.button;

import com.intellij.ide.ui.laf.darcula.DarculaUIUtil;
import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.actionSystem.Toggleable;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.impl.ActionButtonWithText;
import com.intellij.openapi.actionSystem.impl.PresentationFactory;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.ui.dsl.builder.DslComponentProperty;
import com.intellij.ui.dsl.builder.EmptySpacingConfiguration;
import com.intellij.ui.dsl.builder.SpacingConfiguration;
import com.intellij.ui.dsl.builder.VerticalComponentGap;
import com.intellij.ui.dsl.builder.components.SegmentedButtonBorder;
import com.intellij.ui.dsl.gridLayout.HorizontalAlign;
import com.intellij.ui.dsl.gridLayout.UnscaledGaps;
import com.intellij.ui.dsl.gridLayout.VerticalAlign;
//import com.intellij.ui.dsl.gridLayout.builders.RowsGridBuilder;
import com.intellij.ui.dsl.gridLayout.GridLayout;
import com.intellij.ui.scale.JBUIScale;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;

import com.linkoog.devtools.intellij.ui.RowsGridBuilder;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;

import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.EventListener;
import java.util.Objects;

public class SegmentedButton<T> extends JPanel {

    private SpacingConfiguration spacing = new EmptySpacingConfiguration();

    private final List<T> items;
    private T selectedItem = null;

    public SegmentedButton(@NotNull List<T> items) {
        super(new GridLayout());

        this.items = new ArrayList<>(items);
        setFocusable(true);
        setBorder(new SegmentedButtonBorder());  ;
        putClientProperty(DslComponentProperty.VISUAL_PADDINGS, createUnscaledGaps());
        putClientProperty(DslComponentProperty.VERTICAL_COMPONENT_GAP, new VerticalComponentGap(true, true));

        addFocusListener(new FocusListener(){

            @Override
            public void focusGained(FocusEvent e) {
                repaint();
            }

            @Override
            public void focusLost(FocusEvent e) {
                repaint();
            }
        });

        AnAction actionLeft = DumbAwareAction.create(event -> {  moveSelection(-1);});
        actionLeft.registerCustomShortcutSet(ActionUtil.getShortcutSet("SegmentedButton-left"), this);

        AnAction actionRight = DumbAwareAction.create(event -> { moveSelection(1);});
        actionRight.registerCustomShortcutSet(ActionUtil.getShortcutSet("SegmentedButton-right"), this);

        rebuild();
    }


    public void setSpacing(SpacingConfiguration spacing) {
        this.spacing = spacing;
        rebuild();
    }

    public void setSelectedItem(T selectedItem) {
        if ( ! Objects.equals(this.selectedItem, selectedItem)) {
            setSelectedState(this.selectedItem, false);
            setSelectedState(selectedItem, true);
            this.selectedItem = selectedItem;
            for (ModelListener listener : listenerList.getListeners(ModelListener.class)) {
                listener.onItemSelected();
            }
            repaint();
        }
    }

    public void addModelListener(ModelListener l) {
        listenerList.add(ModelListener.class, l);
    }

    public void removeModelListener(ModelListener l) {
        listenerList.remove(ModelListener.class, l);
    }

    public void setEnabled(Boolean enabled) {
        super.setEnabled(enabled);

        for (Component component : getComponents()) {
            component.setEnabled(enabled);
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        // Paint selected button frame over all children
        Graphics2D g2 = (Graphics2D)g.create();
        try {
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
            g2.setPaint(getSegmentedButtonBorderPaint(this, true));
            Component selectedButton = getOrNullComponent(items.indexOf(selectedItem));
            if (selectedButton != null) {
                Rectangle r = selectedButton.getBounds();
                JBInsets.addTo(r, JBUI.insets((int)DarculaUIUtil.LW.getUnscaled()));
                paintBorder(g2, r);
            }
        } finally {
            g2.dispose();
        }
    }

    public void paintBorder(Graphics2D g, Rectangle r) {
        Path2D.Float border = new Path2D.Float(Path2D.WIND_EVEN_ODD);
        float lw = DarculaUIUtil.LW.getFloat();
        float arc = DarculaUIUtil.BUTTON_ARC.getFloat();
        border.append(new RoundRectangle2D.Float(r.x, r.y, r.width, r.height, arc, arc), false);
        arc = Math.max(arc - lw, 0f);
        border.append(new RoundRectangle2D.Float(r.x + lw, r.y + lw, r.width - lw * 2, r.height - lw * 2, arc, arc), false);
        g.fill(border);
    }

    public Paint getSegmentedButtonBorderPaint(Component segmentedButton, Boolean subButton) {
        if (!segmentedButton.isEnabled()) {
            return JBUI.CurrentTheme.Button.disabledOutlineColor();
        }

        if (segmentedButton.hasFocus()) {
            return JBUI.CurrentTheme.Button.focusBorderColor(false);
        }
        else {
            if (subButton) {
                return new GradientPaint(0f, 0f, JBUI.CurrentTheme.SegmentedButton.SELECTED_START_BORDER_COLOR,
                        0f, segmentedButton.getHeight(), JBUI.CurrentTheme.SegmentedButton.SELECTED_END_BORDER_COLOR);
            } else {
                return new GradientPaint(0f, 0f, JBUI.CurrentTheme.Button.buttonOutlineColorStart(false),
                        0f,  segmentedButton.getHeight(), JBUI.CurrentTheme.Button.buttonOutlineColorEnd(false));
            }
        }
    }

    public void rebuild() {
        removeAll();
        PresentationFactory presentationFactory = new PresentationFactory();
        RowsGridBuilder builder = new RowsGridBuilder(this, null);
        for (T item : items) {
            MySegmentedButtonAction action = new MySegmentedButtonAction(this, item, item.toString(), "", null, true);
            MySegmentedButton button = new MySegmentedButton(action, presentationFactory.getPresentation(action), spacing);

            builder.cell(button, 1, HorizontalAlign.FILL, VerticalAlign.CENTER,false, true, null, null, null, null);
        }

        for (ModelListener listener : listenerList.getListeners(ModelListener.class)) {
            listener.onRebuild();
        }
    }

    private void setSelectedState(T item, Boolean selectedState) {
        int componentIndex = items.indexOf(item);
        Component component = getOrNullComponent(componentIndex);
        if (component != null){
            MySegmentedButton mySegmentedButton = (MySegmentedButton) component;
            mySegmentedButton.setSelectedState(selectedState);
        }
    }

    private void moveSelection(int step) {
        if (items.isEmpty()) {
            return;
        }

        int selectedIndex = items.indexOf(selectedItem);
        int newSelectedIndex =  (selectedIndex < 0) ?  0 : newSelectedIndex(selectedIndex + step);
        selectedItem = items.get(newSelectedIndex);
    }

    private int newSelectedIndex(int index){
        if (index < items.size()) return index;
        return index % items.size();
    }


    private UnscaledGaps createUnscaledGaps(){
       int size =  (int) Math.round(DarculaUIUtil.BW.getUnscaled());
        return new UnscaledGapsImpl(size, size, size, size);
    }

    private Component getOrNullComponent(int componentIndex){
        if (componentIndex < 0) return null;
        Component[] components = getComponents();
        if (components == null || componentIndex > components.length )  return null;
        return components[componentIndex];
    }


    public static interface ModelListener extends EventListener {
        default void onItemSelected() {}

        default void onRebuild() {}
    }

    @Getter
    private static class UnscaledGapsImpl implements UnscaledGaps{

        private final int top;
        private final int bottom;
        private final int left;
        private final int right;

        public UnscaledGapsImpl(int top, int left,int bottom,int right) {
            this.top = top;
            this.bottom = bottom;
            this.left = left;
            this.right = right;
        }

        @Override
        public UnscaledGaps copy(int top, int left,int bottom,int right) {
            return new UnscaledGapsImpl(top, left, bottom, right);
        }
    }

    private class MySegmentedButtonAction<T> extends ToggleAction implements DumbAware {
        private SegmentedButton<T> parent;
        private T item;
        private String text;
        private String description;
        private Icon icon;
        private Boolean enabled;

        public MySegmentedButtonAction(SegmentedButton parent, T item, String text, String description, Icon icon, boolean enabled) {
            super(text, description, icon);
            this.parent = parent;
            this.item = item;
            this.text = text;
            this.description = description;
            this.icon = icon;
            this.enabled = enabled;
        }

        public void update(AnActionEvent e) {
            super.update(e);
            e.getPresentation().setEnabled(enabled);
        }

        public boolean isSelected(AnActionEvent e) {
            return Objects.equals(parent.selectedItem, item);
        }

        @Override
        public void setSelected(@NotNull AnActionEvent anActionEvent, boolean state) {
            if (state) {
                parent.setSelectedItem(item);
            }
        }

        public ActionUpdateThread getActionUpdateThread() {
            return ActionUpdateThread.EDT;
        }
    }


    private class MySegmentedButton<T> extends ActionButtonWithText {
        private MySegmentedButtonAction<T> action;
        private Presentation presentation;
        private SpacingConfiguration spacing;

        private boolean selectedState;

        public MySegmentedButton(MySegmentedButtonAction<T> action, Presentation presentation, SpacingConfiguration spacing){
            super(action, presentation, "SegmentedButton", new Dimension(0, 0));
            this.action = action;
            this.presentation = presentation;
            this.spacing = spacing;
        }

        public void actionPerformed(AnActionEvent event) {
            super.actionPerformed(event);

            // Restore toggle action if selected button pressed again
            setSelectedState(action.parent.selectedItem == action.item);
        }

        @Override
        public void setToolTipText(String toolTipText) {
            setCustomToolTipText(toolTipText);
        }

        public boolean isSelectedState() {
            return Toggleable.isSelected(myPresentation);
        }

        public void setSelectedState(boolean selectedState) {
            this.selectedState = selectedState;
            Toggleable.setSelected(myPresentation, selectedState);
        }

        @Override
        public Dimension getPreferredSize()  {
            Dimension preferredSize = super.getPreferredSize();
            int height = Math.max(preferredSize.height + JBUIScale.scale(spacing.getSegmentedButtonVerticalGap()) * 2, buttonMinimumSize().height - JBUIScale.scale(2));
            return new Dimension(preferredSize.width + JBUIScale.scale(spacing.getSegmentedButtonHorizontalGap()) * 2, height);
        }


        public Dimension buttonMinimumSize() {
            return JBUI.size("Button.minimumSize", JBUI.size(72, 24));
        }
    }

}










